package com.dingup.paike.service.auxiliary.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dingup.api.Util.DateUtil;
import com.dingup.api.adapter.user.SysUserAdapter;
import com.dingup.api.enums.StudentCampusEnum;
import com.dingup.api.model.sys.SysDept;
import com.dingup.api.model.sys.SysUser;
import com.dingup.api.template.message.util.DingTalkUtil;
import com.dingup.paike.bean.aobean.TeacherAvailableTimeAO;
import com.dingup.paike.bean.dtobean.PkLessonInfoDTO;
import com.dingup.paike.bean.dtobean.TeacherProcessInstanceDTO;
import com.dingup.paike.bean.querybean.TeacherAvailableTimeQuery;
import com.dingup.paike.bean.querybean.TimeInterval;
import com.dingup.paike.cache.ProcessInstanceRedisCache;
import com.dingup.paike.feign.client.subject.SubjectFeignClient;
import com.dingup.paike.feign.client.user.UserFeignClient;
import com.dingup.paike.service.auxiliary.AvailableTimeService;
import com.dingup.paike.service.pk.PkLessonUserService;
import com.dingup.paike.util.BigDecimalUtil;
import com.dingup.paike.util.TeacherAvailableTimeUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.dingup.api.template.message.util.DingTalkConstUtil.*;
import static com.dingup.paike.util.ConstUtil.TIME_GO_TO_WORK;
import static com.dingup.paike.util.ConstUtil.TIME_OFF_DUTY;

/**
 * Created by wancheng on 2018/11/30.
 */
@Service
public class AvailableTimeServiceImpl implements AvailableTimeService {

    private static final Integer DATE_TIME_EXTEND = 30;
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String HOUR_FORMAT = "HH:mm";
    private static final String PROCESS_UNIT_DAY = "DAY";
    private static final String PROCESS_UNIT_HOUR = "HOUR";
    private static final Double PROCESS_HOUR = 8D;
    @Autowired
    SubjectFeignClient subjectFeignClient;
    @Autowired
    UserFeignClient userFeignClient;
    @Autowired
    PkLessonUserService pkLessonUserService;

    @Override
    public List<Map<String, Object>> getTeacherAvailableTime(TeacherAvailableTimeQuery query) {
        List<TimeInterval> timeIntervals = query.getTime();
        List<String> shanghaiTeacherIds = new ArrayList<>();
        List<String> beijingTeacherIds = new ArrayList<>();
        List<SysUserAdapter> users = new ArrayList<>();
        if (StringUtils.isBlank(query.getTeacherId())) {
            users = subjectFeignClient.getUserBySubjectId(query.getSysSubjectId());
        } else {
            SysUserAdapter user = userFeignClient.getUserByUserId(query.getTeacherId());
            users.add(user);
        }
        Map<String, String> userNameMap = users.stream().collect(Collectors.toMap(SysUserAdapter::getUserId, SysUser::getUserName));
        Map<String, List<Long>> userDeptMap = users.stream()
                .collect(Collectors.toMap(SysUserAdapter::getUserId, u -> u.getDepts().stream().map(SysDept::getId).collect(Collectors.toList())));
        userDeptMap.entrySet().forEach(
                e -> {
                    if (e.getValue().contains(StudentCampusEnum.SHANGHAI.getId())) {
                        shanghaiTeacherIds.add(e.getKey());
                    } else {
                        //过滤外部员工
                        if (!e.getValue().contains(2L)) {
                            beijingTeacherIds.add(e.getKey());
                        }
                    }
                }
        );
        //老师请假时间
        Set<TeacherProcessInstanceDTO> allProcess = new HashSet<>();
        timeIntervals.forEach(
                t -> {
                    Set<TeacherProcessInstanceDTO> shanghaiInstances = getTeacherProcess(shanghaiTeacherIds, t.getBefore(), t.getAfter(), true);
                    Set<TeacherProcessInstanceDTO> beijingInstances = getTeacherProcess(beijingTeacherIds, t.getBefore(), t.getAfter(), false);
                    Set<TeacherProcessInstanceDTO> dtos = new HashSet<TeacherProcessInstanceDTO>();
                    if (!CollectionUtils.isEmpty(shanghaiInstances)) {
                        dtos.addAll(shanghaiInstances);
                    }
                    if (!CollectionUtils.isEmpty(beijingInstances)) {
                        dtos.addAll(beijingInstances);
                    }
                    Date finalStartTime = t.getBefore();
                    dtos = dtos.stream()
                            .filter(dto -> {
                                Date fromDate = new Date(dto.getFromTime());
                                if (DateUtil.date2Str(finalStartTime, DATE_FORMAT).equals(DateUtil.date2Str(fromDate, DATE_FORMAT))) {
                                    return true;
                                } else {
                                    return false;
                                }
                            }).collect(Collectors.toSet());
                    allProcess.addAll(dtos);
                }
        );
        List<String> teacherIds = users.stream().map(SysUserAdapter::getUserId).collect(Collectors.toList());

        //老师上课占用时间
        List<PkLessonInfoDTO> pkLessonInfos = pkLessonUserService.getByUserIdAndStartTimeAndAfterTime(teacherIds, timeIntervals);

        //查询条件转为 map
        Map<String, TimeInterval> timeIntervalMap = timeIntervals.stream().collect(Collectors.toMap(t -> DateUtil.date2Str(t.getBefore(), DATE_FORMAT), t -> t));
        Map<String, Map<String, List<TimeInterval>>> processTimeIntervalMap = processToTimeInterval(allProcess, query.getBefore(), query.getAfter());
        Map<String, Map<String, List<TimeInterval>>> lessonTimeIntervalMap = lessonToTimeInterval(pkLessonInfos, query.getBefore(), query.getAfter());
        List<TimeInterval> all = new ArrayList<>();
        List<SysUserAdapter> finalUser = users;
        timeIntervalMap.entrySet().forEach(
                e -> {
                    TimeInterval time = e.getValue();
                    String day = DateUtil.date2Str(time.getBefore(), DATE_FORMAT);
                    finalUser.forEach(
                            user -> {
                                List<TimeInterval> allUse = new ArrayList<TimeInterval>();
                                List<TimeInterval> process = new ArrayList<TimeInterval>();
                                if (processTimeIntervalMap.get(day) != null) {
                                    process = processTimeIntervalMap.get(day).get(user.getUserId());
                                }
                                List<TimeInterval> lesson = new ArrayList<TimeInterval>();
                                if (lessonTimeIntervalMap.get(day) != null) {
                                    lesson = lessonTimeIntervalMap.get(day).get(user.getUserId());
                                }
                                if (!CollectionUtils.isEmpty(process)) {
                                    allUse.addAll(process);
                                }
                                if (!CollectionUtils.isEmpty(lesson)) {
                                    allUse.addAll(lesson);
                                }
                                List<TimeInterval> availableTime = TeacherAvailableTimeUtil.getAvailableTime(time, allUse, user.getUserId());
                                all.addAll(availableTime);
                            }
                    );
                }
        );
        //可用时间结构整理
        all.forEach(
                t -> {
                    t.setUserName(userNameMap.get(t.getUserId()));
                    t.setHourBefore(DateUtil.getOnlyTimeFormatStr(t.getBefore()));
                    t.setHourAfter(DateUtil.getOnlyTimeFormatStr(t.getAfter()));
                    t.setHour(BigDecimalUtil.getClassHour(t.getBefore(), t.getAfter()));
                });
        Map<TeacherAvailableTimeAO, List<TimeInterval>> allGroup = all.stream()
                .collect(Collectors.groupingBy(
                        t -> new TeacherAvailableTimeAO(DateUtil.date2Str(t.getBefore(), "yyyy"),
                                DateUtil.date2Str(t.getBefore(), "MM"),
                                DateUtil.date2Str(t.getBefore(), "dd"),
                                t.getUserName())
                ));
        allGroup.entrySet().forEach(
                e -> {
                    List<TimeInterval> value = e.getValue();
                    //过滤时间为0的对象
                    value = value.stream().filter(t -> t.getHour().doubleValue() != 0).collect(Collectors.toList());
                    e.getKey().setTimeIntervals(value);
                }
        );
        Map<String, List<TeacherAvailableTimeAO>> aos = allGroup.keySet().stream()
                .collect(Collectors.groupingBy(key -> key.getYear() + "年" + key.getMonth() + "月"));
        List<Map<String, Object>> res = new ArrayList<>();
        aos.entrySet().forEach(
                e -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("month", e.getKey());
                    map.put("data", e.getValue());
                    res.add(map);
                }
        );
        return res;

    }

    private Map<String, Map<String, List<TimeInterval>>> processToTimeInterval(Set<TeacherProcessInstanceDTO> allProcess, String before, String after) {
        // key->2018-10-10 value->list
        Map<String, List<TeacherProcessInstanceDTO>> processMap = allProcess.stream().collect(Collectors.groupingBy(t -> DateUtil.date2Str(new Date(t.getFromTime()), DATE_FORMAT)));
        //转为统一类
        Map<String, Map<String, List<TimeInterval>>> processTimeIntervalMap = new HashMap<>();
        processMap.entrySet().forEach(
                e -> {
                    //key->userId  value->list
                    Map<String, List<TimeInterval>> timeMap = e.getValue().stream().map(p -> {
                        return new TimeInterval(getBefore(new Date(p.getFromTime()), before, p.getUnit(), p.getDurationInHour()), getAfter(new Date(p.getToTime()), after, p.getUnit(), p.getDurationInHour()), p.getUserId(), p.getUnit());
                    })
                            .collect(Collectors.groupingBy(TimeInterval::getUserId));
                    processTimeIntervalMap.put(e.getKey(), timeMap);
                }
        );
        return processTimeIntervalMap;
    }

    private Map<String, Map<String, List<TimeInterval>>> lessonToTimeInterval(List<PkLessonInfoDTO> pkLessonInfos, String before, String after) {
        // key->2018-10-10 value->list
        Map<String, List<PkLessonInfoDTO>> lessonMap = pkLessonInfos.stream().collect(Collectors.groupingBy(p -> DateUtil.date2Str(p.getBeginTime(), DATE_FORMAT)));
        Map<String, Map<String, List<TimeInterval>>> lessonTimeIntervalMap = new HashMap<>();
        lessonMap.entrySet().forEach(
                e -> {
                    Map<String, List<TimeInterval>> timeMap = e.getValue().stream().map(p -> new TimeInterval(getBefore(p.getBeginTime(), before, PROCESS_UNIT_HOUR, null), getAfter(p.getEndTime(), after, PROCESS_UNIT_HOUR, null), p.getTeacherId(), PROCESS_UNIT_HOUR))
                            .collect(Collectors.groupingBy(TimeInterval::getUserId));
                    lessonTimeIntervalMap.put(e.getKey(), timeMap);
                }
        );
        return lessonTimeIntervalMap;
    }

    @Override
    public Set<TeacherProcessInstanceDTO> getTeacherProcess(List<String> userIds, Date startTime, Date endTime, Boolean ifShanghai) {
        if (CollectionUtils.isEmpty(userIds)) {
            return null;
        }
        startTime = DateUtils.addDays(startTime, -DATE_TIME_EXTEND);
        endTime = DateUtils.addDays(endTime, DATE_TIME_EXTEND);
        String userIdString = userIds.stream().collect(Collectors.joining(","));
        String processCode;
        if (ifShanghai) {
            processCode = DINGTALK_PROCESS_CODE_SAHNGHAI;
        } else {
            processCode = DINGTALK_PROCESS_CODE_BEIJING;
        }
        List<String> processInstanceIds = DingTalkUtil.getProcessInstanceIds(startTime.getTime(), endTime.getTime(), userIdString, processCode);
        Set<TeacherProcessInstanceDTO> teacherProcessInstanceDTOs = new HashSet<>();
        processInstanceIds.forEach(
                id -> {
                    List<TeacherProcessInstanceDTO> instances = ProcessInstanceRedisCache.getInstance().getProcessInsance(id);
                    if (!CollectionUtils.isEmpty(instances)) {
                        teacherProcessInstanceDTOs.addAll(instances);
                        return;
                    }
                    JSONObject res = DingTalkUtil.getProcessInstanceById(id);
                    if (res != null) {
                        String userId = String.valueOf(res.get(USER_ID));
                        List<Map<String, Object>> detailList = (List<Map<String, Object>>) res.get(DETAIL_LIST);
                        String unit = res.get(UNIT).toString();
                        for (Map<String, Object> e : detailList) {
                            TeacherProcessInstanceDTO dto = JSON.parseObject(JSON.toJSONString(e.get("approveInfo")), TeacherProcessInstanceDTO.class);
                            dto.setUserId(userId);
                            dto.setProcessInstanceId(id);
                            dto.setUnit(unit);
                            instances.add(dto);
                        }
                        ProcessInstanceRedisCache.getInstance().setProcessInsance(id, instances);
                        teacherProcessInstanceDTOs.addAll(instances);
                    }
                }
        );

        return teacherProcessInstanceDTOs;
    }

    private Date getDefaultDate(Date date, Boolean ifBefore) {
        String day = DateUtil.date2Str(date, DATE_FORMAT);
        String defaultTime = "";
        if (ifBefore) {
            defaultTime = day + " " + TIME_GO_TO_WORK;
        } else {
            defaultTime = day + " " + TIME_OFF_DUTY;
        }
        return DateUtil.str2Date(defaultTime);
    }

    private Date getBefore(Date date, String before, String unit, Double durationInHour) {
        String day = DateUtil.date2Str(date, DATE_FORMAT);
        String defaultTime = day + " " + before;
        Date beforeTime = DateUtil.str2Date(defaultTime);
        //说明请假一整天
        if (PROCESS_UNIT_DAY.equals(unit) && durationInHour == PROCESS_HOUR) {
            return beforeTime;
        }
        if (date.before(beforeTime)) {
            return beforeTime;
        } else {
            return date;
        }
    }

    private Date getAfter(Date date, String after, String unit, Double durationInHour) {
        String day = DateUtil.date2Str(date, DATE_FORMAT);
        String defaultTime = day + " " + after;
        Date afterTime = DateUtil.str2Date(defaultTime);
        if (PROCESS_UNIT_DAY.equals(unit) && durationInHour == PROCESS_HOUR) {
            return afterTime;
        }
        if (date.before(afterTime)) {
            return date;
        } else {
            return afterTime;
        }
    }
}
